-------------------------------------------------------
--              ROBERT JOHNSON'S LUA UTILS           --
-------------------------------------------------------

luautils = {};

-- startWith java style !
luautils.stringStarts = function(String,Start)
	return string.sub(String, 1, string.len(Start)) == Start;
end

luautils.stringEnds = function(String, End)
	return String:sub(-End:len()) == End;
end

luautils.trim = function(s)
  return (s:gsub("^%s*(.-)%s*$", "%1"))
end

-- split java style
luautils.split = function(pString, pPattern)
   local Table = {};
   local fpat = "(.-)" .. pPattern;
   local last_end = 1;
   local s, e, cap = pString:find(fpat, 1);
   while s do
      if s ~= 1 or cap ~= "" then
		table.insert(Table,cap);
      end
      last_end = e+1;
      s, e, cap = pString:find(fpat, last_end);
   end
   if last_end <= #pString then
      cap = pString:sub(last_end);
      table.insert(Table, cap);
   end
   return Table
end

function luautils.indexOf(table1, value)
	for i,v in ipairs(table1) do
		if v == value then
			return i
		end
	end
	return -1
end

-- get all the tile in the range of the startingGrid
luautils.getNextTiles = function(cell, startingGrid, range)
	local result = {};
	local rangeX = startingGrid:getX() - range;
	if(rangeX < 0) then
		rangeX = 0;
	end
	local rangeY = startingGrid:getY() - range;
	if(rangeY < 0) then
		rangeY = 0;
	end
	local rangeX2 = startingGrid:getX() + range;
	local rangeY2 = startingGrid:getY() + range;
	for x2=rangeX, rangeX2 do
		for y2=rangeY, rangeY2 do
			local nextGrid = cell:getGridSquare(x2, y2, startingGrid:getZ());
			if nextGrid then
				table.insert(result, #result, nextGrid);
			end
		end
	end
	return result;
end

function luautils.walkAdj(playerObj, square, keepActions)
	if not keepActions then
		ISTimedActionQueue.clear(playerObj);
	end
--	if not AdjacentFreeTileFinder.isTileOrAdjacent(playerObj:getCurrentSquare(), square) then
		-- Avoid walking to already near spot
		square = luautils.getCorrectSquareForWall(playerObj, square);
		local diffX = math.abs(square:getX() + 0.5 - playerObj:getX());
		local diffY = math.abs(square:getY() + 0.5 - playerObj:getY());
		if diffX <= 1.6 and diffY <= 1.6 then
			return true;
		end
		local adjacent = AdjacentFreeTileFinder.Find(square, playerObj);
		if adjacent ~= nil then
			ISTimedActionQueue.add(ISWalkToTimedAction:new(playerObj, adjacent));
			return true;
		else
			return  false;
		end
--	else
--		return true;
--	end
end

function luautils.walkAdjAltTest(playerObj, square, altSquare, keepActions)
	if not keepActions then
		ISTimedActionQueue.clear(playerObj);
	end
	if altSquare then
		local diffX = math.abs(square:getX() + 0.5 - altSquare:getX());
		local diffY = math.abs(square:getY() + 0.5 - altSquare:getY());
		if diffX <= 1.6 and diffY <= 1.6 then
			return true;
		end
	end
	square = luautils.getCorrectSquareForWall(playerObj, square);
	local adjacent = AdjacentFreeTileFinder.Find(square, playerObj);
	if adjacent ~= nil then
		ISTimedActionQueue.add(ISWalkToTimedAction:new(playerObj, adjacent));
		return true;
	else
		return  false;
	end
end

-- taking other side of the wall if we're behind a collidable item
luautils.getCorrectSquareForWall = function(playerObj, square)
    if square:Is(IsoFlagType.collideW) and playerObj:getX() < square:getX() then
        return getCell():getGridSquare(square:getX() + 1, square:getY(), square:getZ());
    end
    if square:Is(IsoFlagType.collideN) and playerObj:getY() < square:getY() then
        return getCell():getGridSquare(square:getX(), square:getY() + 1, square:getZ());
    end
    return square;
end

function luautils.walkAdjWall(playerObj, square, north, keepActions)
	if not keepActions then
		ISTimedActionQueue.clear(playerObj);
	end
--	if not AdjacentFreeTileFinder.isTileOrAdjacent(playerObj:getCurrentSquare(), square) then
		local adjacent = AdjacentFreeTileFinder.FindWall(square, north, playerObj);
		if adjacent ~= nil then
			ISTimedActionQueue.add(ISWalkToTimedAction:new(playerObj, adjacent));
			return true;
		else
			return  false;
		end
--	else
--		return true;
--	end
end

function luautils.walkAdjWindowOrDoor(playerObj, square, item, keepActions)
	if not keepActions then
		ISTimedActionQueue.clear(playerObj);
	end
--	if not AdjacentFreeTileFinder.isTileOrAdjacent(playerObj:getCurrentSquare(), square) then
		local adjacent = AdjacentFreeTileFinder.FindWindowOrDoor(square, item, playerObj);
		if adjacent ~= nil then
			if adjacent == playerObj:getCurrentSquare() then
				return true;
			end
			ISTimedActionQueue.add(ISWalkToTimedAction:new(playerObj, adjacent));
			return true;
		else
			return  false;
		end
--	else
--		return true;
--	end
end

function luautils.walkToContainer(container, playerNum)
	if container:getType() == "floor" then
		return true
	end
	local playerObj = getSpecificPlayer(playerNum)
	if container:getParent() and not container:getVehiclePart() and container:getParent():getSquare():DistToProper(playerObj:getCurrentSquare()) < 2 then
		return true;
	end

	if container:isInCharacterInventory(playerObj) then
		return true
	end
	local isoObject = container:getParent()
	if not isoObject or not isoObject:getSquare() then
		return true
	end
	if instanceof(isoObject, "BaseVehicle") then
		if playerObj:getVehicle() == isoObject then
			return true
		end
		if playerObj:getVehicle() then
			error "luautils.walkToContainer()"
		end
		local part = container:getVehiclePart()
		if part and part:getArea() then
			if part:getVehicle():canAccessContainer(part:getIndex(), playerObj) then
				return true
			end
			if part:getDoor() and part:getInventoryItem() then
				-- TODO: open the door if needed
			end
			ISTimedActionQueue.add(ISPathFindAction:pathToVehicleArea(playerObj, part:getVehicle(), part:getArea()))
			return true
		end
		error "luautils.walkToContainer()"
	end
	if instanceof(isoObject, "IsoDeadBody") then
		return true
	end
	
	local adjacent = AdjacentFreeTileFinder.Find(isoObject:getSquare(), playerObj)
	if not adjacent then
		return false
	end
	if adjacent == playerObj:getCurrentSquare() then
		return true
	end
	ISTimedActionQueue.clear(playerObj)
	ISTimedActionQueue.add(ISWalkToTimedAction:new(playerObj, adjacent))
	return true
end

function luautils.haveToBeTransfered(player, item, dontWalk)
	if item and item:getContainer() ~= nil and item:getContainer() ~= player:getInventory() then
		if dontWalk then return true; end
		luautils.walkToContainer(item:getContainer(), player:getPlayerNum())
		return true
	else
		return false;
	end
end


function round(num, idp)
  local mult = 10^(idp or 0)
  return math.floor(num * mult + 0.5) / mult
end

function luautils.round(num, idp)
    return round(num,idp);
end

function luautils.updatePerksXp(perks, player)
	local level = player:getPerkLevel(perks);
	player:getXp():setXPToLevel(perks, level);
end

-------------------------------------------------------
--              ROBOMAT'S LUA UTILS           --
-------------------------------------------------------

---
-- This function tries to equip the passed items as
-- primary or secondary items. It will return the items
-- that had been originally equipped.
-- Alternatively you can pass on Strings (e.g.: "Base.Screwdriver") and
-- the function will try to find that item in the player's inventory and equip it.
--
-- @param _player - The player who equips the items.
-- @param _primItemToEquip - The item to equip in the primary slot.
-- @param _scndItemToEquip - The item to equip in the secondary slot.
--
-- @author RoboMat
--
function luautils.equipItems(_player, _primItemToEquip, _scndItemToEquip)
    local player = _player;
    local primItem = _primItemToEquip;
    local scndItem = _scndItemToEquip;

    -- If we didn't receive an actual item we use the string
    -- to find an appropriate item in the inventory.
    if type(primItem) == 'string' then
        primItem = player:getInventory():FindAndReturn(primItem);
    end
    if type(scndItem) == 'string' then
        scndItem = player:getInventory():FindAndReturn(scndItem);
    end

    -- Store the currently equipped items.
    local storePrim = player:getPrimaryHandItem();
    local storeScnd = player:getSecondaryHandItem();

    -- Equip the new items if necessary.
    if primItem then
        if not storePrim or storePrim ~= primItem then
            ISTimedActionQueue.add(ISEquipWeaponAction:new(player, primItem, 25, true));
        end
    end
    if scndItem then
        if not storeScnd or storeScnd ~= scndItem then
            ISTimedActionQueue.add(ISEquipWeaponAction:new(player, scndItem, 25, false));
        end
    end

    -- Return the stored items in case we want to re-equip them later on.
    return storePrim, storeScnd;
end


---
-- Shows a modal window that informs the player about something and only has
-- an okay button to be closed.
--
-- @param _text - The text to display on the modal
-- @param _centered - If set to true the modal will be centered (optional)
-- @param _width - The width of the window (optional)
-- @param _height - The height of the window (optional)
--
-- @author RoboMat
--
function luautils.okModal(_text, _centered, _width, _height, _posX, _posY)
    local posX = _posX or 0;
    local posY = _posY or 0;
    local width = _width or 230;
    local height = _height or 120;
    local centered = _centered;
    local txt = _text;
    local core = getCore();

    -- center the modal if necessary
    if centered then
        posX = core:getScreenWidth() * 0.5 - width * 0.5;
        posY = core:getScreenHeight() * 0.5 - height * 0.5;
    end

    local modal = ISModalDialog:new(posX, posY, width, height, txt, false, nil, nil);
    modal:initialise();
    modal:addToUIManager();
end

---
-- Based on the walkTo function from RJ's luautils.
--
-- @param _player - Player who should move his buttocks.
-- @param _object - The object / tile to walk to.
-- @param _cancelTA - Determines wether timed actions should be canceled
-- before the player starts to walk.
--
-- @author RoboMat
-- @since 2.0.0
--
function luautils.walkToObject(_player, _object, _cancelTA)
    local player = _player;
    local object = _object;
    local tile = _object:getSquare();
    local cancel = _cancelTA;

    -- Abort all current Timed Actions.
    if cancel then
        ISTimedActionQueue.clear(player);
    end

    -- Pathfinding and starting the actual walking.
    if not AdjacentFreeTileFinder.isTileOrAdjacent(player:getCurrentSquare(), tile) then
        local adjacent = AdjacentFreeTileFinder.FindWindowOrDoor(tile, object, player);
        if adjacent then
            ISTimedActionQueue.add(ISWalkToTimedAction:new(player, adjacent));
            return true;
        end
        return false;
    end
    return true;
end

---
-- Ported the WeaponLowerCondition(...) function of the
-- SwipeState.class to lua. Use this to damage your weapons.
-- If the weapon breaks during the execution of this function
-- it checks in which hand the item was equipped and tries to
-- replace it with the next best weapon.
--
-- @param _weapon - The weapon / item to damage.
-- @param _character - The player who is carrying the weapon.
-- @param _replace - (Optional) Wether or not to replace the item if it is broken.
-- @param _chance - (Optional) The chance for the weapon to be damaged. If omitted it uses the conditionLowerChance of the item.
--
-- @author RoboMat
--
function luautils.weaponLowerCondition(_weapon, _character, _replace, _chance)
    local weapon = _weapon;
    local chance = _chance or weapon:getConditionLowerChance();

    -- Random chance to damage the weapon based on the stats.
    if ZombRand(chance) == 0 then
        local replace = _replace or true;
        local condition = weapon:getCondition() - 1;
        weapon:setCondition(condition);

        -- If the weapon breaks unequip it and get a new one instead.
        if condition <= 0 and replace then
            local char = _character;
            local descriptor = char:getDescriptor();
            local newWeapon = char:getInventory():getBestWeapon(descriptor);

            -- Checks in which hand the item was equipped.
            local pos = luautils.isEquipped(weapon, char);

            -- Replace it with the new weapon.
            if pos == 1 then
                ISTimedActionQueue.add(ISEquipWeaponAction:new(char, newWeapon, 25, true));
            elseif pos == 2 then
                ISTimedActionQueue.add(ISEquipWeaponAction:new(char, newWeapon, 25, false));
            elseif pos == 3 then -- TODO
                ISTimedActionQueue.add(ISEquipWeaponAction:new(char, newWeapon, 25, false));
            end
        end
    end
end

---
-- Checks if the item is equipped as the primary or
-- secondary weapon of the player. Returns 1 if the
-- weapon is the primary, 2 if it is the secondary,
-- 3 if it is equipped in both hands at the same time
-- and zero if it is not equipped.
--
-- @param _item - The item to check for.
-- @param _player - The player to search through.
--
-- @return 1 if in primary hand.
-- @return 2 if in secondary hand.
-- @return 3 if in both hands.
-- @return 0 if not equipped.
--
-- @author RoboMat
--
function luautils.isEquipped(_item, _player)
    local p = _player;
    local i = _item;
    local prim = p:getPrimaryHandItem();
    local scnd = p:getSecondaryHandItem();

    if prim == i and scnd == i then
        return 3;
    elseif prim == i then
        return 1;
    elseif scnd == i then
        return 2;
    end
    return 0;
end

function luautils.split(inputstr, sep)
    if sep == nil then
        sep = "%s"
    end
    local t={} ; i=1
    for str in string.gmatch(inputstr, "([^"..sep.."]+)") do
        t[i] = str
        i = i + 1
    end
    return t
end

function luautils.getConditionRGB(condition)
	local r = ((100 - condition) / 100) ;
	local g = (condition / 100);
	return {r = r, g = g, b = 0};
end

---
--checks if two squares are adjacent to one another
--
--@param _square1
--@param _square2
--
--@return true if adjacent
--@return false if not adjacent
--
--@author eris
--
function luautils.isSquareAdjacentToSquare(_square1, _square2)
	if not (_square1 and _square2) then return false; end;
	local squares = {
		n = _square1:getN(),
		s = _square1:getS(),
		e = _square1:getE(),
		w = _square1:getW(),
	};
	for _, square in pairs(squares) do
		if square and (square == _square2) then return true; end;
	end;
	return false;
end